package gui;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Accordion;
import javafx.scene.control.Button;
import javafx.scene.control.Dialogs;
import javafx.scene.control.Dialogs.DialogOptions;
import javafx.scene.control.Dialogs.DialogResponse;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Callback;
import jpsearch.JPSearchRuleGenerator;

import org.apache.commons.lang3.StringUtils;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.input.JDOMParseException;
import org.jdom2.input.SAXBuilder;

import translation.ManyToOne;
import translation.OneToMany;
import translation.OneToOne;
import translation.VarBinding;

public class RuleController implements javafx.fxml.Initializable {

	// Reference to the main application
	private Main mainApp;
	private Stage dialogStage;

	final static int ONETOONE = 89;
	final static int ONETOMANY = 90;
	final static int MANYTOONE = 91;	
	final static int NONE = -1;

	private static OneToOne currentNewOneToOne = null;
	private static OneToMany currentNewOneToMany = null;
	private static ManyToOne currentNewManyToOne = null;
	private static int currentRuleType = NONE; //So to easily identify what type the current rule is
	private JPSearchRuleGenerator jpsGen = new JPSearchRuleGenerator();
	private static ObservableList<Element> xmlElements = FXCollections.observableArrayList();
	private static ObservableList<JPSearchElement> jpsearchElements = FXCollections.observableArrayList();
	private ObservableList<Element> originalXmlElements = FXCollections.observableArrayList();
	private ObservableList<JPSearchElement> originalJpsearchElements = FXCollections.observableArrayList();
	private ObservableList<String> addedItems = FXCollections.observableArrayList();
	private static File xmlInstance; //= new File("test_intance.xml");
	private String fromFormat = "";

	//Static objects
	public static int xmlElementsSelected = 0;
	public static int jpsearchElementsSelected = 0;
	public static List<String> xmlElementsSelection = new ArrayList<String>();
	public static List<String> jpsearchElementsSelection = new ArrayList<String>();
	public static boolean created = false;
	public static boolean edited = false;

	/*
	 * FXML Elements 
	 */
	@FXML private TableView<Element> tableXMLElements;
	@FXML private TableView<JPSearchElement> tableJPSearchElements;
	@FXML private TableView<VarBinding> tableVariableBinding;

	@FXML public static Label lblRuleType;
	@FXML private Label lblFromXPath;
	@FXML private Label lblToXPath;
	@FXML private Label lblKeyToMap;

	@FXML private TextField txtFromRegex;
	@FXML private TextField txtToRegex;
	@FXML private TextField txtPrefix;
	@FXML private TextField txtPostfix;
	@FXML private TextField txtListBinding;
	@FXML private TextField txtExplicitBinding;
	@FXML private TextField txtCustomElement;

	@FXML private Button btnCancel;
	@FXML private Button btnSaveToMap;
	@FXML public static Button btnCreate;
	@FXML private Button btnDeleteRule;
	@FXML private Button btnSaveVarBinding;
	@FXML private Button btnDeleteVarBinding;
	@FXML private Button btnAddCustomElement;
	@FXML private Button btnAddToXMLRule;
	@FXML private Button btnAddToJPSRule;

	@FXML private Accordion accAdvanced;

	@FXML private ListView<String> lstAddedElements;

	@FXML private MenuItem menuItemSaveToXML;
	@FXML private MenuItem menuLoadWithAttr;
	@FXML private MenuItem menuLoadWithoutAttr;

	@FXML private TreeView<String> treeJPSElements;
	@FXML private TreeView<String> treeXmlElements;

	/*-------------------------------------------------*/

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void initialize(URL arg0, ResourceBundle arg1) {
		tableJPSearchElements.setDisable(false);
		resetInterface();
		clearAdvancedFields();

		/*
		 * XML Elements Table initialization 
		 */		
		TableColumn checkedCol = new TableColumn<Element, Boolean>();
		checkedCol.setMaxWidth(20); 
		checkedCol.setCellValueFactory(new PropertyValueFactory("checked")); 
		checkedCol.setCellFactory(new Callback<TableColumn<Element, Boolean>, TableCell<Element, Boolean>>() { 
			public TableCell<Element, Boolean> call(TableColumn<Element, Boolean> p) { 
				return new CheckBoxTableCell<Element, Boolean>(); 
			} 
		});		

		TableColumn nameCol = new TableColumn(); 
		nameCol.setText("Name"); 
		nameCol.setCellValueFactory(new PropertyValueFactory("name"));		
		nameCol.prefWidthProperty().bind(tableXMLElements.widthProperty().divide(1));

		tableXMLElements.setItems(xmlElements); 
		tableXMLElements.setEditable(true); 
		tableXMLElements.getColumns().clear();
		tableXMLElements.getColumns().addAll(checkedCol, nameCol);
		//------------------------------------------------

		/*
		 * JPSearch Elements initialization
		 */
		loadJPSearchElements();

		TableColumn checkedJPSearchCol = new TableColumn<JPSearchElement, Boolean>();
		checkedJPSearchCol.setMaxWidth(20); 
		checkedJPSearchCol.setCellValueFactory(new PropertyValueFactory("checked")); 
		checkedJPSearchCol.setCellFactory(new Callback<TableColumn<JPSearchElement, Boolean>, TableCell<JPSearchElement, Boolean>>() { 
			public TableCell<JPSearchElement, Boolean> call(TableColumn<JPSearchElement, Boolean> p) { 
				return new CheckBoxTableCell<JPSearchElement, Boolean>(); 
			} 
		});

		TableColumn nameJPSearchCol = new TableColumn(); 
		nameJPSearchCol.setText("Name"); 
		nameJPSearchCol.setCellValueFactory(new PropertyValueFactory("name"));		
		nameJPSearchCol.prefWidthProperty().bind(tableXMLElements.widthProperty().divide(1));

		tableJPSearchElements.setItems(jpsearchElements); 
		tableJPSearchElements.setEditable(true); 
		tableJPSearchElements.getColumns().clear();
		tableJPSearchElements.getColumns().addAll(checkedJPSearchCol, nameJPSearchCol);

		lstAddedElements.setItems(addedItems);

		//---------------------------------------------------------------

		/*
		 * Advanced section event handlers
		 */
		//FROM REGEX	
		txtFromRegex.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2){
					switch(currentRuleType){
					case ONETOONE:	
						currentNewOneToOne.setFromRegEx(txtFromRegex.getText());
						break;

					case ONETOMANY:	
						currentNewOneToMany.setFromRegEx(txtFromRegex.getText());
						break;

					case MANYTOONE:
						if(tableXMLElements.getSelectionModel().getSelectedItem() != null){
							//get the selected element of table with XML elements
							String fromxpath = ((Element)tableXMLElements.getSelectionModel().getSelectedItem()).nameProperty().get();
							currentNewManyToOne.addFromRegex(fromxpath, txtFromRegex.getText());
						}
						break;
					}
				}
			}
		});

		//TO REGEX
		txtToRegex.focusedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
				// Handling only when focus is out.
				if(!arg2){
					switch(currentRuleType){
					case ONETOONE:	
						currentNewOneToOne.setToRegEx(txtToRegex.getText());
						break;

					case ONETOMANY:	
						if(tableJPSearchElements.getSelectionModel().getSelectedItem() != null){
							//get the selected element of table with XML elements
							String fromxpath = ((JPSearchElement)tableJPSearchElements.getSelectionModel().getSelectedItem()).nameProperty().get();
							currentNewOneToMany.addToField(fromxpath, txtToRegex.getText());
						}						
						break;

					case MANYTOONE:
						currentNewManyToOne.setToRegEx(txtToRegex.getText());
						break;
					}
				}
			}
		});

		/*
		 * VARIABLE BINDINGS
		 */		
		txtPrefix.textProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(final ObservableValue<? extends String> observable,
					final String oldValue,
					final String newValue) {
				if(!newValue.equals("")){
					btnSaveVarBinding.setDisable(false);
				}

				//If prefix is empty AND all the other textfields disable save button
				else if(newValue.equals("") && 
						txtPostfix.getText().equals("") && 
						txtListBinding.getText().equals("") && 
						txtExplicitBinding.getText().equals("")){
					btnSaveVarBinding.setDisable(true);
				}
			}
		});

		txtPostfix.textProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(final ObservableValue<? extends String> observable,
					final String oldValue,
					final String newValue) {
				if(!newValue.equals("")){
					btnSaveVarBinding.setDisable(false);
				}

				//If prefix is empty AND all the other textfields disable save button
				else if(newValue.equals("") && 
						txtPrefix.getText().equals("") && 
						txtListBinding.getText().equals("") && 
						txtExplicitBinding.getText().equals("")){
					btnSaveVarBinding.setDisable(true);
				}
			}
		});

		txtListBinding.textProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(final ObservableValue<? extends String> observable,
					final String oldValue,
					final String newValue) {
				if(!newValue.equals("")){
					btnSaveVarBinding.setDisable(false);
				}

				//If prefix is empty AND all the other textfields disable save button
				else if(newValue.equals("") && 
						txtPrefix.getText().equals("") && 
						txtPostfix.getText().equals("") && 
						txtExplicitBinding.getText().equals("")){
					btnSaveVarBinding.setDisable(true);
				}
			}
		});

		txtExplicitBinding.textProperty().addListener(new ChangeListener<String>() {
			@Override
			public void changed(final ObservableValue<? extends String> observable,
					final String oldValue,
					final String newValue) {
				if(!newValue.equals("")){
					btnSaveVarBinding.setDisable(false);
				}

				//If prefix is empty AND all the other textfields disable save button
				else if(newValue.equals("") && 
						txtPrefix.getText().equals("") && 
						txtPostfix.getText().equals("") && 
						txtListBinding.getText().equals("")){
					btnSaveVarBinding.setDisable(true);
				}
			}
		});

		/*
		 * Table Variable Binding
		 */

		//Prefix
		TableColumn prefixCol = new TableColumn(); 
		prefixCol.setText("Prefix"); 	
		prefixCol.prefWidthProperty().bind(tableVariableBinding.widthProperty().divide(4));		
		prefixCol.setEditable(true);

		prefixCol.setCellValueFactory(new Callback<CellDataFeatures<VarBinding, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<VarBinding, String> p) {
				if(p.getValue().getExplicitPrefixBinding() != null){
					String s = p.getValue().getExplicitPrefixBinding().get(0);
					return new ReadOnlyObjectWrapper(s);
				}

				return new ReadOnlyObjectWrapper("");
			}
		});

		prefixCol.setCellFactory(TextFieldTableCell.forTableColumn());
		prefixCol.setOnEditCommit(
				new EventHandler<CellEditEvent<VarBinding, String>>() {
					@Override
					public void handle(CellEditEvent<VarBinding, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						t.getTableView().getItems().get(rowSelected).addExplicitPrefixBinding(t.getNewValue());
					}
				});

		//Postfix
		TableColumn postfixCol = new TableColumn(); 
		postfixCol.setText("Postfix"); 
		postfixCol.prefWidthProperty().bind(tableVariableBinding.widthProperty().divide(4));		
		postfixCol.setEditable(true);

		postfixCol.setCellValueFactory(new Callback<CellDataFeatures<VarBinding, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<VarBinding, String> p) {
				if(p.getValue().getExplicitPostfixBinding() != null){
					String s = p.getValue().getExplicitPostfixBinding().get(0);
					return new ReadOnlyObjectWrapper(s);
				}
				return new ReadOnlyObjectWrapper("");
			}
		});

		postfixCol.setCellFactory(TextFieldTableCell.forTableColumn());
		postfixCol.setOnEditCommit(
				new EventHandler<CellEditEvent<VarBinding, String>>() {
					@Override
					public void handle(CellEditEvent<VarBinding, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						VarBinding vb = t.getTableView().getItems().get(rowSelected);
						vb.addExplicitPostfixBinding(t.getNewValue());
						System.out.println(vb.getExplicitPostfixBinding().get(0));
					}
				});

		//List Binding
		TableColumn listBindingCol = new TableColumn(); 
		listBindingCol.setText("List Binding"); 		
		listBindingCol.prefWidthProperty().bind(tableVariableBinding.widthProperty().divide(4));		
		listBindingCol.setEditable(true);

		listBindingCol.setCellValueFactory(new Callback<CellDataFeatures<VarBinding, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<VarBinding, String> p) {
				String s = p.getValue().getListBinding();
				if(s != null)
					return new ReadOnlyObjectWrapper(s);

				return new ReadOnlyObjectWrapper("");
			}
		});

		listBindingCol.setCellFactory(TextFieldTableCell.forTableColumn());
		listBindingCol.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<VarBinding, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<VarBinding, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setListBinding(t.getNewValue());
					}
				});

		//Explicit Binding
		TableColumn explicitBindingCol = new TableColumn(); 
		explicitBindingCol.setText("Explicit Binding"); 		
		explicitBindingCol.prefWidthProperty().bind(tableVariableBinding.widthProperty().divide(4));		
		explicitBindingCol.setEditable(true);

		explicitBindingCol.setCellValueFactory(new Callback<CellDataFeatures<VarBinding, String>, ObservableValue<String>>() {
			public ObservableValue<String> call(CellDataFeatures<VarBinding, String> p) {
				String s = p.getValue().getExplicitBinding();
				if(s != null)
					return new ReadOnlyObjectWrapper(s);

				return new ReadOnlyObjectWrapper("");
			}
		});

		explicitBindingCol.setCellFactory(TextFieldTableCell.forTableColumn());
		explicitBindingCol.setOnEditCommit(
				new EventHandler<TableColumn.CellEditEvent<VarBinding, String>>() {
					@Override
					public void handle(TableColumn.CellEditEvent<VarBinding, String> t) {
						int rowSelected = t.getTablePosition().getRow();
						(t.getTableView().getItems().get(rowSelected)).setExplicitBinding(t.getNewValue());
					}
				});

		tableVariableBinding.getColumns().clear();
		tableVariableBinding.getColumns().addAll(prefixCol, postfixCol, listBindingCol, explicitBindingCol);
		tableVariableBinding.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

		tableVariableBinding.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<VarBinding>() {

			@Override
			public void changed(ObservableValue<? extends VarBinding> arg0,
					VarBinding arg1, VarBinding arg2) {
				btnDeleteVarBinding.setDisable(false);				
			}
		});

		//----------------------------------------------------------------

		// Listen for List selection changes
		lstAddedElements.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {

			@Override
			public void changed(ObservableValue<? extends String> arg0,
					String arg1, String arg2) { //arg2: the one you want

				//Case of selection clearing
				if(lstAddedElements.getSelectionModel().getSelectedItem() == null)
					return;

				Object o = jpsGen.getRule(arg2);
				if(o instanceof OneToOne){
					currentNewOneToOne = (OneToOne)o;
					currentRuleType = ONETOONE;
				}
				else if(o instanceof OneToMany){
					currentNewOneToMany = (OneToMany)o;
					currentRuleType = ONETOMANY;
				}
				else if(o instanceof ManyToOne){
					currentNewManyToOne = (ManyToOne)o;
					currentRuleType = MANYTOONE;
				}
				loadOriginalData();
				showRuleDetails();
				edited = true;
			}
		});

		// Listen for tableXMLElements selection changes
		tableXMLElements.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Element>() {

			@Override
			public void changed(ObservableValue<? extends Element> arg0,
					Element arg1, Element arg2) {

				//Only in ManyToOne case show the selected element's details
				if(arg2 != null && currentRuleType == MANYTOONE && created){
					String selectedElem = arg2.nameProperty().get();
					clearAdvancedFields();

					if(!xmlElementsSelection.contains(selectedElem)){
						setDisableAdvancedFields(true);
					}
					else{
						//if not exist is the case that the ManyToOne has just been created
						if(currentNewManyToOne.getFromRegEx().containsKey(selectedElem) == false){
							currentNewManyToOne.addFromRegex(selectedElem, null);						
						}

						//else just show the details
						else{	
							String fromregex = currentNewManyToOne.getFromRegEx().get(selectedElem);
							txtFromRegex.setText(fromregex);
							List<VarBinding> vbList = currentNewManyToOne.getVariableBinding().get(selectedElem);
							tableVariableBinding.getItems().clear();
							if(vbList != null)
								for(VarBinding vb : vbList){
									tableVariableBinding.getItems().add(vb);
								}							
						}

						lblFromXPath.setText(selectedElem);	
						setDisableAdvancedFields(false);
					}
				}
			}	
		});

		// Listen for tableXMLElements selection changes
		tableJPSearchElements.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<JPSearchElement>() {

			@Override
			public void changed(
					ObservableValue<? extends JPSearchElement> arg0,
					JPSearchElement arg1, JPSearchElement arg2) {

				//Only in OneToMany case show the selected element's details
				if(arg2 != null && currentRuleType == ONETOMANY){
					txtToRegex.setDisable(false);
					String selectedElem = arg2.nameProperty().get();

					if(!jpsearchElementsSelection.contains(selectedElem)){
						txtToRegex.setDisable(true);
					}
					else{
						//if not exist is the case that the ManyToOne has just been created
						if(currentNewOneToMany.getToFields().containsKey(selectedElem) == false){
							currentNewOneToMany.addToField(selectedElem, null);						
						}

						//else just show the details
						else{						
							String toregex = currentNewOneToMany.getToFields().get(selectedElem);
							txtToRegex.setText(toregex);
						}

						lblToXPath.setText(selectedElem);	
						txtToRegex.setDisable(false);
					}
				}				
			}	
		});

		treeXmlElements.setOnMouseClicked(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent mouseEvent) {
				if(mouseEvent.getButton().equals(MouseButton.PRIMARY)){
					if(mouseEvent.getClickCount() == 2){
						handleAddToXMLRule();
					}
				}
			}
		});

		treeJPSElements.setOnMouseClicked(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent mouseEvent) {
				if(mouseEvent.getButton().equals(MouseButton.PRIMARY)){
					if(mouseEvent.getClickCount() == 2){
						handleAddToJPSRule();
					}
				}
			}
		});

	}

	/*
	 * Element static class used in XML Elements and JPSearch possible elements
	 */
	public static class Element {

		private BooleanProperty checked; 
		private StringProperty name;         

		private Element(boolean checked, String name) { 

			this.checked = new SimpleBooleanProperty(checked); 
			this.name = new SimpleStringProperty(name);             
			this.checked.addListener(new ChangeListener<Boolean>() { 
				public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
					if(!created){
						if(t1 == true) {
							xmlElementsSelected++;
							xmlElementsSelection.add(nameProperty().get());
						}
						else{
							xmlElementsSelected--;
							xmlElementsSelection.remove(nameProperty().get());
						}

						if(xmlElementsSelected == 1 && jpsearchElementsSelected == 1){
							lblRuleType.setText("OneToOne");
							currentRuleType = ONETOONE;
							btnCreate.setDisable(false);
						}					
						else if(xmlElementsSelected == 1 && jpsearchElementsSelected >= 2){
							lblRuleType.setText("OneToMany");
							currentRuleType = ONETOMANY;
							btnCreate.setDisable(false);
						}                    	
						else if(xmlElementsSelected >= 2 && jpsearchElementsSelected == 1){
							lblRuleType.setText("ManyToOne");
							currentRuleType = MANYTOONE;
							btnCreate.setDisable(false);
						}
						else if(xmlElementsSelected >= 2 && jpsearchElementsSelected >= 2){
							lblRuleType.setText("Invalid Selection. ManyToMany not supported");
							btnCreate.setDisable(true);
						}
						else{
							lblRuleType.setText("Select Items");
							currentRuleType = NONE;
						}
					} 
				}
			});            
		}

		public BooleanProperty checkedProperty() { return checked; }
		public StringProperty nameProperty() { return name; }

		public void setCheckedProperty(boolean checked){
			this.checked = new SimpleBooleanProperty(checked); 
		}
		public void setNameProperty(String name){
			this.name = new SimpleStringProperty(name);
		}

	}

	//JPSearchElement
	public static class JPSearchElement {

		private BooleanProperty checked; 
		private StringProperty name;         

		private JPSearchElement(boolean checked, String name) { 

			this.checked = new SimpleBooleanProperty(checked); 
			this.name = new SimpleStringProperty(name);             
			this.checked.addListener(new ChangeListener<Boolean>() { 
				public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) { 
					if(!created){
						//Add checked item to the list for easier later use
						if(t1 == true) {
							jpsearchElementsSelected++;
							jpsearchElementsSelection.add(nameProperty().get());
						}
						else{
							jpsearchElementsSelected--;
							jpsearchElementsSelection.remove(nameProperty().get());
						}

						if(xmlElementsSelected == 1 && jpsearchElementsSelected == 1){
							lblRuleType.setText("OneToOne");
							currentRuleType = ONETOONE;
							btnCreate.setDisable(false);
						}					
						else if(xmlElementsSelected == 1 && jpsearchElementsSelected >= 2){
							lblRuleType.setText("OneToMany");
							currentRuleType = ONETOMANY;
							btnCreate.setDisable(false);
						}                    	
						else if(xmlElementsSelected >= 2 && jpsearchElementsSelected == 1){
							lblRuleType.setText("ManyToOne");
							currentRuleType = MANYTOONE;
							btnCreate.setDisable(false);
						}
						else if(xmlElementsSelected >= 2 && jpsearchElementsSelected >= 2){
							lblRuleType.setText("Invalid Selection. ManyToMany not supported");
							btnCreate.setDisable(true);

						}
						else{
							lblRuleType.setText("Select Items");
							currentRuleType = NONE;
						}
					}
				}
			});            
		}

		public BooleanProperty checkedProperty() { return checked; }
		public StringProperty nameProperty() { return name; }

		public void setCheckedProperty(boolean checked){
			this.checked = new SimpleBooleanProperty(checked); 
		}
		public void setNameProperty(String name){
			this.name = new SimpleStringProperty(name);
		}
	}

	/*
	 * Create new rules and set them a current modified elements.
	 * Handler events will automatically update the elements in the rule objects
	 */

	/**
	 * Creates a new OneToOne rule | set as currentModifying element
	 * @param fromXPath
	 * @param toXPath
	 */
	public void createNewOneToOne(String fromXPath, String toXPath){
		currentNewOneToOne = new OneToOne(fromXPath, toXPath);
		currentRuleType = ONETOONE;
	}

	/**
	 * Creates a new OneToMany rule | set as currentModifying element
	 * @param fromXPath
	 * @param fromRegEx
	 */
	public void createNewOneToMany(String fromXPath, String fromRegEx){
		currentNewOneToMany = new OneToMany(fromXPath, fromRegEx);
		for(String s : jpsearchElementsSelection){
			currentNewOneToMany.addToField(s, null);
		}

		currentRuleType = ONETOMANY;
	}

	/**
	 * Creates a new Many To One rule type 
	 * @param fromXPath The XPath that will be used as reference for the structure (usually, if not always, the first element from the list)
	 * @param toXPath To XPath to be added to the structure
	 * @param toRegEx To Regex to be added to the structure
	 */
	public void createNewManyToOne( String toXPath, String toRegEx){
		currentNewManyToOne = new ManyToOne(toXPath, toRegEx);
		/*for(String s: fromXPaths){
			currentNewManyToOne.addFromRegex(s, null);			
		}*/
		currentRuleType = MANYTOONE;
	}

	/**
	 * Adds an XML Element from the tree to the XML Element Table
	 */
	@FXML 
	private void handleAddToXMLRule(){
		if(treeXmlElements.getSelectionModel().getSelectedItem() == null){
			Dialogs.showWarningDialog(dialogStage, "Select an XML Element", "Warning", "Rule Generator");
			return;
		}

		TreeItem<String> item = treeXmlElements.getSelectionModel().getSelectedItem();
		if(!item.getChildren().isEmpty()) return;
		String path = "";
		while(item.getParent() != null){
			path = "/" + item.getValue() + path;
			item = item.getParent();
		}
		//path = path.substring(1);

		if(xmlElementsSelection.contains(path))
			return;

		Element newElem = new Element(true, path);
		xmlElements = tableXMLElements.getItems();
		xmlElements.add(newElem);		

		xmlElementsSelected++;			
		xmlElementsSelection.add(path);

		if(xmlElementsSelected == 1 && jpsearchElementsSelected == 1){
			lblRuleType.setText("OneToOne");
			currentRuleType = ONETOONE;
			btnCreate.setDisable(false);
		}					
		else if(xmlElementsSelected == 1 && jpsearchElementsSelected >= 2){
			lblRuleType.setText("OneToMany");
			currentRuleType = ONETOMANY;
			btnCreate.setDisable(false);
		}                    	
		else if(xmlElementsSelected >= 2 && jpsearchElementsSelected == 1){
			lblRuleType.setText("ManyToOne");
			currentRuleType = MANYTOONE;
			btnCreate.setDisable(false);
		}
		else if(xmlElementsSelected >= 2 && jpsearchElementsSelected >= 2){
			lblRuleType.setText("Invalid Selection. ManyToMany not supported");
			btnCreate.setDisable(true);
		}
		else{
			lblRuleType.setText("Select Items");
			currentRuleType = NONE;
		}
	}


	/**
	 * Adds a JPS Element from the tree to the JPSearch Element Table
	 */
	@FXML 
	private void handleAddToJPSRule(){
		if(treeJPSElements.getSelectionModel().getSelectedItem() == null){
			Dialogs.showWarningDialog(dialogStage, "Select a JPSearch Element", "Warning", "Rule Generator");
			return;
		}
		else if(treeXmlElements.getRoot() == null)
			return;

		TreeItem<String> item = treeJPSElements.getSelectionModel().getSelectedItem();
		if(!item.getChildren().isEmpty()) return;
		String path = "";
		while(item.getParent() != null){
			path = "/" + item.getValue() + path;
			item = item.getParent();
		}
		path = "/"  + item.getValue() + path;

		if(jpsearchElementsSelection.contains(path))
			return;

		JPSearchElement newJPS = new JPSearchElement(true, path);
		jpsearchElements = tableJPSearchElements.getItems();
		jpsearchElements.add(newJPS);

		jpsearchElementsSelected++;
		jpsearchElementsSelection.add(path);

		if(xmlElementsSelected == 1 && jpsearchElementsSelected == 1){
			lblRuleType.setText("OneToOne");
			currentRuleType = ONETOONE;
			btnCreate.setDisable(false);
		}					
		else if(xmlElementsSelected == 1 && jpsearchElementsSelected >= 2){
			lblRuleType.setText("OneToMany");
			currentRuleType = ONETOMANY;
			btnCreate.setDisable(false);
		}                    	
		else if(xmlElementsSelected >= 2 && jpsearchElementsSelected == 1){
			lblRuleType.setText("ManyToOne");
			currentRuleType = MANYTOONE;
			btnCreate.setDisable(false);
		}
		else if(xmlElementsSelected >= 2 && jpsearchElementsSelected >= 2){
			lblRuleType.setText("Invalid Selection. ManyToMany not supported");
			btnCreate.setDisable(true);
		}
		else{
			lblRuleType.setText("Select Items");
			currentRuleType = NONE;
		}
	}

	/*
	 * Initialize JPSearch Elements - Hard Coded to avoid external files
	 */
	private void loadJPSearchElements(){

		TreeItem<String> rootItem = new TreeItem<String>("JPSearchCore");
		rootItem.setExpanded(true);

		rootItem.getChildren().add(new TreeItem<String>("Identifier"));    

		TreeItem<String> modifiers = new TreeItem<String>("Modifiers");
		modifiers.getChildren().add(new TreeItem<String>("GivenName"));
		modifiers.getChildren().add(new TreeItem<String>("FamilyName"));
		rootItem.getChildren().add(modifiers);

		TreeItem<String> creators = new TreeItem<String>("Creators");
		creators.getChildren().add(new TreeItem<String>("GivenName"));
		creators.getChildren().add(new TreeItem<String>("FamilyName"));
		rootItem.getChildren().add(creators);

		TreeItem<String> publisher = new TreeItem<String>("Publisher");
		TreeItem<String> pname = new TreeItem<String>("PersonName");
		TreeItem<String> orginfo = new TreeItem<String>("OrganizationInformation");
		TreeItem<String> address = new TreeItem<String>("Address");

		pname.getChildren().add(new TreeItem<String>("GivenName"));
		pname.getChildren().add(new TreeItem<String>("FamilyName"));
		orginfo.getChildren().add(new TreeItem<String>("Name"));
		orginfo.getChildren().add(address);
		address.getChildren().add(new TreeItem<String>("Name"));
		address.getChildren().add(new TreeItem<String>("Description"));
		publisher.getChildren().add(pname);
		publisher.getChildren().add(orginfo);
		rootItem.getChildren().add(publisher);

		rootItem.getChildren().add(new TreeItem<String>("CreationDate"));
		rootItem.getChildren().add(new TreeItem<String>("ModifiedDate"));
		rootItem.getChildren().add(new TreeItem<String>("Description"));

		TreeItem<String> rightsDescription = new TreeItem<String>("RightsDescription");
		rightsDescription.getChildren().add(new TreeItem<String>("RightsDescriptionInformation"));
		rightsDescription.getChildren().add(new TreeItem<String>("Description"));
		rightsDescription.getChildren().add(new TreeItem<String>("ActualRightsDescriptionReference"));
		rightsDescription.getChildren().add(new TreeItem<String>("ActualRightsDescription"));
		rootItem.getChildren().add(rightsDescription);

		TreeItem<String> source = new TreeItem<String>("Source");
		source.getChildren().add(new TreeItem<String>("SourceElementType"));
		TreeItem<String> sourceElement = new TreeItem<String>("SourceElement");
		sourceElement.getChildren().add(new TreeItem<String>("SourceElementTitle"));
		sourceElement.getChildren().add(new TreeItem<String>("SourceElementDescription"));
		sourceElement.getChildren().add(new TreeItem<String>("SourceElementIdentifier"));
		source.getChildren().add(sourceElement);
		source.getChildren().add(new TreeItem<String>("CreationMethod"));
		source.getChildren().add(new TreeItem<String>("CreationDescription"));
		rootItem.getChildren().add(source);

		rootItem.getChildren().add(new TreeItem<String>("Keyword"));
		rootItem.getChildren().add(new TreeItem<String>("Title"));
		rootItem.getChildren().add(new TreeItem<String>("PreferenceValue"));
		rootItem.getChildren().add(new TreeItem<String>("CollectionLabel"));

		TreeItem<String> rating = new TreeItem<String>("Rating");
		rating.getChildren().add(new TreeItem<String>("LabelDefinition"));
		rating.getChildren().add(new TreeItem<String>("LabelValue"));
		rootItem.getChildren().add(rating);

		TreeItem<String> oii = new TreeItem<String>("OriginalImageIdentifier");
		oii.getChildren().add(new TreeItem<String>("Identifier"));
		oii.getChildren().add(new TreeItem<String>("OriginationOfID"));
		rootItem.getChildren().add(oii);

		TreeItem<String> gps = new TreeItem<String>("GPSPositioning");
		gps.getChildren().add(new TreeItem<String>("longitude"));
		gps.getChildren().add(new TreeItem<String>("latitude"));
		gps.getChildren().add(new TreeItem<String>("altitude"));
		gps.getChildren().add(new TreeItem<String>("dim"));
		rootItem.getChildren().add(gps);

		TreeItem<String> roi = new TreeItem<String>("RegionOfInterest");
		roi.getChildren().add(new TreeItem<String>("RegionLocator"));
		roi.getChildren().add(new TreeItem<String>("Description"));
		roi.getChildren().add(new TreeItem<String>("Keyword"));
		roi.getChildren().add(new TreeItem<String>("Title"));

		TreeItem<String> contDescr = new TreeItem<String>("ContentDescription");
		TreeItem<String> person = new TreeItem<String>("Person");
		TreeItem<String> personName = new TreeItem<String>("Name");
		TreeItem<String> personAffiliation = new TreeItem<String>("Affiliation");
		TreeItem<String> roiAddress = new TreeItem<String>("Address");
		roiAddress.getChildren().add(new TreeItem<String>("Name"));
		roiAddress.getChildren().add(new TreeItem<String>("Description"));		
		personName.getChildren().add(new TreeItem<String>("GivenName"));
		personName.getChildren().add(new TreeItem<String>("FamilyName"));
		person.getChildren().add(personName);				
		personAffiliation.getChildren().add(new TreeItem<String>("Name"));
		TreeItem<String> roiAffAddress = new TreeItem<String>("Address");
		roiAffAddress.getChildren().add(new TreeItem<String>("Name"));
		roiAffAddress.getChildren().add(new TreeItem<String>("Description"));
		personAffiliation.getChildren().add(roiAffAddress);		
		person.getChildren().add(personAffiliation);
		person.getChildren().add(roiAddress);
		person.getChildren().add(new TreeItem<String>("Description"));
		person.getChildren().add(new TreeItem<String>("Nationality"));		
		contDescr.getChildren().add(person);

		TreeItem<String> object = new TreeItem<String>("Object");
		object.getChildren().add(new TreeItem<String>("Name"));
		object.getChildren().add(new TreeItem<String>("Description"));
		object.getChildren().add(new TreeItem<String>("Label"));
		contDescr.getChildren().add(object);

		TreeItem<String> roiPlace = new TreeItem<String>("Place");
		roiPlace.getChildren().add(new TreeItem<String>("Name"));
		roiPlace.getChildren().add(new TreeItem<String>("Description"));
		contDescr.getChildren().add(roiPlace);	

		TreeItem<String> event = new TreeItem<String>("Event");
		event.getChildren().add(new TreeItem<String>("Label"));
		event.getChildren().add(new TreeItem<String>("Description"));
		contDescr.getChildren().add(event);

		roi.getChildren().add(contDescr);

		TreeItem<String> extDescr = new TreeItem<String>("ExternalDescription");
		extDescr.getChildren().add(new TreeItem<String>("TagName"));
		extDescr.getChildren().add(new TreeItem<String>("TagName/fromNamespace"));
		extDescr.getChildren().add(new TreeItem<String>("TagName/fromNamespacePrefix"));
		extDescr.getChildren().add(new TreeItem<String>("LiteralValue"));
		extDescr.getChildren().add(new TreeItem<String>("LiteralValue/fromControlledVocabularyOfLiterals"));
		extDescr.getChildren().add(new TreeItem<String>("LiteralValue/fromGrammarOfLiterals"));

		extDescr.getChildren().add(new TreeItem<String>("ResourceValue"));
		extDescr.getChildren().add(new TreeItem<String>("ResourceValue/fromNamespace"));

		extDescr.getChildren().add(new TreeItem<String>("StructuredValue"));
		extDescr.getChildren().add(new TreeItem<String>("StructuredValue/fromNamespace"));

		roi.getChildren().add(extDescr);

		rootItem.getChildren().add(roi);

		rootItem.getChildren().add(new TreeItem<String>("Width"));
		rootItem.getChildren().add(new TreeItem<String>("Height"));

		treeJPSElements.setRoot(rootItem);
		treeJPSElements.setShowRoot(true);
	}

	/*
	 * Handle methods
	 */

	/**
	 * Opens an XML instance file
	 */
	@FXML
	private void handleOpenXML(){

		FileChooser fileChooserInstance = new FileChooser();
		//Set extension filter
		FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("XML files (*.xml)", "*.xml");
		fileChooserInstance.getExtensionFilters().add(extFilter);

		//set initial directory same as the previous
		if(xmlInstance != null){
			File existDirectory = xmlInstance.getParentFile();
			fileChooserInstance.setInitialDirectory(existDirectory);
		}

		fileChooserInstance.setTitle("Select XML Instance");
		xmlInstance = fileChooserInstance.showOpenDialog(null);
		//xmlInstance = new File("test_intance.xml");

		if(xmlInstance != null){
			tableXMLElements.getItems().clear();
			clearAdvancedFields();
			resetInterface();
			tableJPSearchElements.setDisable(false);
			txtCustomElement.setDisable(false);
			btnAddCustomElement.setDisable(false);
			menuLoadWithoutAttr.setDisable(false);
			menuLoadWithAttr.setDisable(false);
			parseXmlToTree(xmlInstance, false);
		}
	}

	/**
	 * Parses the XML instance and shows the structure into
	 * the TreeView
	 * Uses pathCreator method
	 */
	private void parseXmlToTree(File xml, boolean withAttributes){			
		SAXBuilder builder = new SAXBuilder();
		TreeItem<String> rootItem = new TreeItem<String>("/");
		try {
			Document document = (Document) builder.build(xml);
			org.jdom2.Element rootNode = document.getRootElement();
			List<org.jdom2.Element> elements = rootNode.getChildren();

			for(org.jdom2.Element e : elements){				
				pathCreator(e, rootItem, withAttributes);
			}
			treeXmlElements.setRoot(rootItem);
			treeXmlElements.setShowRoot(true);
			rootItem.setExpanded(true);
		}
		catch(JDOMParseException e1){
			e1.printStackTrace();
			Dialogs.showErrorDialog(dialogStage, "XML could not be parsed. Maybe an XML syntax error.", "Error",
					"Rule Generator");
		}
		catch(Exception e){
			e.printStackTrace();
			Dialogs.showErrorDialog(dialogStage, "XML could not be parsed. Uknown error.", "Error",
					"Rule Generator");
		}
	}

	/**
	 * Recursive method used along with parseXmlToTree
	 * @param currentElement Current element parsed
	 * @param parent The parent element in TreeView
	 */
	/*
	 * TODO: Add the support of attribute value selection 
	 * e.g. <book lang="en">Book</book>
	 *  	/book/@lang => en
	 */
	private void pathCreator(org.jdom2.Element currentElement, TreeItem<String> parent, boolean withAttributes){

		String elemItem = "";
		//tries to write attributes as well, if they exists
		if(withAttributes){
			if(currentElement.getAttributes().isEmpty())
				elemItem = currentElement.getName();
			else
			{
				elemItem  = currentElement.getName() + "[";			
				for(int i = 0; i<currentElement.getAttributes().size(); i++){
					Attribute a = currentElement.getAttributes().get(i);
					if(a.getName().equals("roiid") == false)
						elemItem += "@" + a.getName() + "=\"" + a.getValue() + "\"";
					if(i+1 < currentElement.getAttributes().size())
						elemItem += " and ";
				}
				elemItem += "]";
			}
			
		}
		else{
			elemItem = currentElement.getName();
		}				

		//Remove prexisting same element
		for(int i = 0; i < parent.getChildren().size(); i++){
			String s = parent.getChildren().get(i).getValue();
			if(s.equals(elemItem)){
				return;
			}
		}
		
		TreeItem<String> item = new TreeItem<String>(elemItem);			

		for(org.jdom2.Element e : currentElement.getChildren()){
			pathCreator(e, item, withAttributes);
		}

		parent.getChildren().add(item);
		
		//Show attributes in tree
		if(currentElement.getAttributes().isEmpty() == false){
			for(Attribute a : currentElement.getAttributes()){
				if(a.getName().equals("roiid") == false){
					parent.getChildren().add( new TreeItem<String>(currentElement.getName() + "/@" + a.getName()));
				}
			}
		}
	}

	/**
	 * Reloads the xml elements without attributes
	 */
	@FXML
	private void handleLoadWithoutAttributes(){
		treeXmlElements.setRoot(null);
		parseXmlToTree(xmlInstance, false);
	}

	/**
	 * Reloads the xml elements with attributes
	 */
	@FXML
	private void handleLoadWithAttributes(){
		treeXmlElements.setRoot(null);
		parseXmlToTree(xmlInstance, true);
	}

	/**
	 * Creates a new rule based on the selected items in the tables
	 */
	@FXML
	private void handleCreateRuleType(){
		//Lists to be filled with the selected elements from the tables

		String fromxpath = xmlElementsSelection.get(0);
		String toxpath = jpsearchElementsSelection.get(0);

		switch(currentRuleType){
		case ONETOONE:			
			createNewOneToOne(fromxpath, toxpath);
			lblFromXPath.setText(fromxpath);
			lblToXPath.setText(toxpath);
			setDisableAdvancedFields(false);
			break;

		case ONETOMANY:
			createNewOneToMany(fromxpath, null);
			lblFromXPath.setText(fromxpath);
			lblToXPath.setText("Select an JPSearch element first");	
			setDisableAdvancedFields(false);
			txtToRegex.setDisable(true);
			break;

		case MANYTOONE:
			createNewManyToOne(toxpath, null);
			lblFromXPath.setText("Select an XML element first");
			lblToXPath.setText(toxpath);	
			setDisableAdvancedFields(true);
			break;
		}

		lblKeyToMap.setText("Key: " + fromxpath);
		btnCreate.setDisable(true);
		btnCancel.setDisable(false);
		btnAddCustomElement.setDisable(true);
		btnAddToJPSRule.setDisable(true);
		btnAddToXMLRule.setDisable(true);
		txtCustomElement.setDisable(true);
		btnSaveToMap.setDisable(false);
		accAdvanced.setDisable(false);
		accAdvanced.getPanes().get(0).setExpanded(true);
		lstAddedElements.setDisable(true);
		treeXmlElements.setDisable(true);
		treeJPSElements.setDisable(true);
		created = true;

		//Show only the selected items in tables
		ObservableList<Element> onlySelectedXML = FXCollections.observableArrayList();
		for(Element e : xmlElements){
			if(e.checkedProperty().get() == true){
				onlySelectedXML.add(new Element(true, e.nameProperty().get()));
			}
		}
		tableXMLElements.getItems().clear();
		tableXMLElements.setItems(onlySelectedXML);
		xmlElements = FXCollections.observableArrayList(onlySelectedXML);

		ObservableList<JPSearchElement> onlySelectedJPS = FXCollections.observableArrayList();
		for(JPSearchElement e : jpsearchElements){
			if(e.checkedProperty().get() == true){
				onlySelectedJPS.add(new JPSearchElement(true, e.nameProperty().get()));
			}
		}
		tableJPSearchElements.getItems().clear();
		tableJPSearchElements.setItems(onlySelectedJPS);
		jpsearchElements = FXCollections.observableArrayList(onlySelectedJPS);

	}

	/**
	 * Cancels the current rule type - resets tables
	 */
	@FXML
	private void handleCancel(){
		switch(currentRuleType){
		case ONETOONE:
			currentNewOneToOne = null;
			break;

		case ONETOMANY:
			currentNewOneToMany = null;
			break;

		case MANYTOONE:
			currentNewManyToOne = null;
			break;
		}

		currentRuleType = NONE;

		if(edited){
			resetInterface();
			edited = false;
			return;
		}

		//Change interface		
		setDisableAdvancedFields(true);
		clearAdvancedFields();
		treeJPSElements.setDisable(false);
		treeXmlElements.setDisable(false);
		lstAddedElements.setDisable(false);
		accAdvanced.setDisable(true);
		btnCancel.setDisable(true);
		btnSaveToMap.setDisable(true);
		btnCreate.setDisable(false);
		created = false;		
	}

	/**
	 * Load the original data to the tables
	 */
	private void loadOriginalData(){
		List<Element> tempList = new ArrayList<Element>(originalXmlElements);
		tableXMLElements.getItems().clear();
		for(Element e : tempList){
			xmlElements.add(new Element(false, e.nameProperty().get()));
		}
		tableXMLElements.setItems(xmlElements);

		List<JPSearchElement> tempList2 = new ArrayList<JPSearchElement>(originalJpsearchElements);
		tableJPSearchElements.getItems().clear();
		for(JPSearchElement e : tempList2){
			jpsearchElements.add(new JPSearchElement(false, e.nameProperty().get()));
		}
		tableJPSearchElements.setItems(jpsearchElements);
	}

	/**
	 * Resets the interface components
	 */
	private void resetInterface(){

		loadOriginalData();
		currentRuleType = NONE;
		xmlElementsSelected = 0;
		jpsearchElementsSelected = 0;

		lblRuleType.setText("Select Items");
		lblKeyToMap.setText("Key:");	
		txtToRegex.setDisable(false);

		xmlElementsSelection.clear();
		jpsearchElementsSelection.clear();
		xmlElements.clear();
		jpsearchElements.clear();
		btnAddCustomElement.setDisable(false);
		btnAddToJPSRule.setDisable(false);
		btnAddToXMLRule.setDisable(false);
		txtCustomElement.setDisable(false);
		txtCustomElement.clear();
		lstAddedElements.setDisable(false);
		lstAddedElements.getSelectionModel().clearSelection();

		btnCancel.setDisable(true);
		btnSaveToMap.setDisable(true);
		btnCreate.setDisable(true);
		accAdvanced.setDisable(true);
		treeXmlElements.setDisable(false);
		treeJPSElements.setDisable(false);

		created = false;
		edited = false;
		setDisableAdvancedFields(true);
		clearAdvancedFields();
	}

	/**
	 * Save the current modified element to translated elements Map of RuleGenerator class
	 */
	@FXML
	private void handleSaveToMap(){
		String xPathKey = null;
		List<String> toFields = new ArrayList<String>();

		switch(currentRuleType){
		case ONETOONE:
			xPathKey = currentNewOneToOne.getFromXPath();
			jpsGen.addToTranslatedElements(xPathKey, currentNewOneToOne);
			toFields.add(currentNewOneToOne.getToXPath());
			break;

		case ONETOMANY:
			xPathKey = currentNewOneToMany.getFromXPath();
			jpsGen.addToTranslatedElements(xPathKey, currentNewOneToMany);
			tableJPSearchElements.getSelectionModel().clearSelection();
			toFields.addAll(currentNewOneToMany.getToFields().keySet());
			break;

		case MANYTOONE:
			if(currentNewManyToOne.getFromRegEx().isEmpty()){ //the case that no selection was made during the data filling
				for(String s : xmlElementsSelection){
					currentNewManyToOne.addFromRegex(s, null);
				}
			}

			Iterator<Map.Entry<String, String>> it = currentNewManyToOne.getFromRegEx().entrySet().iterator();
			Map.Entry<String, String> pair = it.next();
			xPathKey = pair.getKey();
			toFields.add(currentNewManyToOne.getToXPath());
			jpsGen.addToTranslatedElements(xPathKey, currentNewManyToOne);
			break;
		}	

		//In case of update
		if(lstAddedElements.getItems().contains(xPathKey))
			lstAddedElements.getItems().remove(xPathKey);

		lstAddedElements.getItems().add(xPathKey);
		lstAddedElements.getSelectionModel().clearSelection();
		btnDeleteRule.setDisable(false);
		menuItemSaveToXML.setDisable(false);	

		resetInterface();
	}	

	/**
	 * Deletes a rule
	 */
	@FXML
	private void handleDeleteRule(){
		String selectedItem = lstAddedElements.getSelectionModel().getSelectedItem();
		if(selectedItem != null && Dialogs.showConfirmDialog(dialogStage, "Are you sure you want to delete this rule?", 
				"Confirmation", "Rule Generator", DialogOptions.YES_NO) == DialogResponse.YES){
			jpsGen.removeRule(selectedItem);
			lstAddedElements.getItems().remove(selectedItem);
			resetInterface();
			if(lstAddedElements.getItems().isEmpty()){
				btnDeleteRule.setDisable(true);
				menuItemSaveToXML.setDisable(true);
			}
		}
	}

	/**
	 * Handles the functionality of the save variable button
	 */
	@FXML
	private void handleSaveVariableBinding(){
		String prefix = txtPrefix.getText();
		String postfix = txtPostfix.getText();
		String listBinding = txtListBinding.getText();
		String explicitBinding = txtExplicitBinding.getText();
		VarBinding newVB = new VarBinding();

		//Fill variable binding with data
		if(!prefix.equals("")){
			newVB.addExplicitPrefixBinding(prefix);
		}

		if(!postfix.equals("")){
			newVB.addExplicitPostfixBinding(postfix);
		}

		if(!listBinding.equals("")){
			newVB.setListBinding(listBinding);
		}

		if(!explicitBinding.equals("")){
			newVB.setExplicitBinding(explicitBinding);
		}

		switch(currentRuleType){
		case ONETOONE:				
			currentNewOneToOne.addVariableBinding(newVB);
			break;

		case ONETOMANY:	
			currentNewOneToMany.addVariableBinding(newVB);				
			break;

		case MANYTOONE:
			if(tableXMLElements.getSelectionModel().getSelectedItem() != null){
				//get the selected element of table with XML elements
				String fromxpath = tableXMLElements.getSelectionModel().getSelectedItem().nameProperty().get();
				currentNewManyToOne.addVarBinding(fromxpath, newVB);
			}
			break;
		}

		tableVariableBinding.getItems().add(newVB); //add to table

		//Clear fields, disable save 
		txtPrefix.setText("");
		txtPostfix.setText("");
		txtListBinding.setText("");
		txtExplicitBinding.setText("");
		btnSaveVarBinding.setDisable(true);
	}

	/**
	 * Handles the functionality of the save variable button
	 */
	@FXML
	private void handleDeleteVariableBinding(){
		VarBinding selectedItem = tableVariableBinding.getSelectionModel().getSelectedItem();
		if(selectedItem != null && Dialogs.showConfirmDialog(dialogStage, "Are you sure you want to delete this binding?",
				"Confirmation", "Rule Generator", DialogOptions.YES_NO) == DialogResponse.YES){
			switch(currentRuleType){
			case ONETOONE:				
				currentNewOneToOne.removeVarBinding(selectedItem);
				break;

			case ONETOMANY:	
				currentNewOneToMany.removeVarBinding(selectedItem);		
				break;

			case MANYTOONE:
				if(tableXMLElements.getSelectionModel().getSelectedItem() != null){
					//get the selected element of table with XML elements
					String fromxpath = tableXMLElements.getSelectionModel().getSelectedItem().nameProperty().get();
					currentNewManyToOne.removeVarBinding(fromxpath, selectedItem);
				}
				break;
			}

			tableVariableBinding.getItems().remove(selectedItem);
		}
	}

	/**
	 * Saves the saved rules to an XML format file
	 */
	@FXML
	private void handleSaveToXML(){
		try {
			FileChooser fileChooserInstance = new FileChooser();

			//Set extension filter
			FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("XML files (*.xml)", "*.xml");
			fileChooserInstance.getExtensionFilters().add(extFilter);

			//set initial directory same as the previous
			if(xmlInstance != null){
				File existDirectory = xmlInstance.getParentFile();
				fileChooserInstance.setInitialDirectory(existDirectory);
			}

			fileChooserInstance.setTitle("Choose name for XML Rules");
			File toSave = fileChooserInstance.showSaveDialog(null);
			if(!toSave.getAbsolutePath().contains(".xml")){
				toSave = new File(toSave.getAbsolutePath() + ".xml");
			}

			jpsGen.writeAllElementsToXMLFile(fromFormat, toSave);
			Dialogs.showInformationDialog(dialogStage, "Rules saved succesfully to file " + toSave.getAbsolutePath(), "Info", "Rule Generator");
		}
		catch(Exception e){
			Dialogs.showErrorDialog(dialogStage, e.getMessage());
		}
	}

	/**
	 * Shows the rules set so far in XML format
	 */
	@FXML
	private void handleShowXML(){
		mainApp.showXmlMetadata(jpsGen.outputAllElementsToXMLFile(fromFormat));
	}

	/**
	 * Sets the from format for the translation rules
	 */
	@FXML
	private void handleSetFromFormat(){
		fromFormat = Dialogs.showInputDialog(dialogStage, "Enter the From Format for this translation rules",
				"From Format", "Rule Generator");
	}

	/**
	 * Adds the custom element to the table
	 */
	@FXML
	private void handleAddCustom(){
		if(txtCustomElement.getText() != null && !StringUtils.isBlank(txtCustomElement.getText())){
			if(txtCustomElement.getText().contains(" ")){
				Dialogs.showErrorDialog(dialogStage, "The custom XML Element must not contain spaces", "Error", "Rule Generator");
				txtCustomElement.requestFocus();
				txtCustomElement.selectAll();
				return;
			}
			Element newEl = new Element(true, txtCustomElement.getText());

			//tableXMLElements.scrollTo(0);
			xmlElements.add(newEl);
			originalXmlElements.add(newEl);
			xmlElementsSelected++;			
			xmlElementsSelection.add(txtCustomElement.getText());

			txtCustomElement.clear();
		}
	}

	/**
	 * Shows the about message
	 */
	@FXML
	private void handleAbout(){
		String msg = "The tool was created by Nicos Demetriou for Jaime Delgado, UPC Barcelona Tech";
		Dialogs.showInformationDialog(dialogStage, msg, "Rule Generator", "About");
	}

	/**
	 * Destroys all data and closes the application
	 */
	@FXML
	private void handleClose(){
		if(Dialogs.showConfirmDialog(null, "Are you sure you want to exit the application?",
				"Confirmation", "Rule Generator",DialogOptions.YES_NO) == DialogResponse.YES){
			resetInterface();
			tableXMLElements.getItems().clear();
			lstAddedElements.getItems().clear();
			jpsGen = new JPSearchRuleGenerator();
			dialogStage.close();
		}
	}

	//------------------------------------------------

	/**
	 * Is called by the main application to give a reference back to itself.
	 * 
	 * @param mainApp
	 */
	public void setMainApp(Main mainApp) {
		this.mainApp = mainApp;
	}

	/**
	 * Sets the stage of this dialog.
	 * @param dialogStage
	 */
	public void setDialogStage(Stage dialogStage) {
		this.dialogStage = dialogStage;
	}

	/**
	 * Enables or Disables the Advanced fields. Used in ManyToOne
	 */
	public void setDisableAdvancedFields(boolean disabled){
		txtFromRegex.setDisable(disabled);
		txtPrefix.setDisable(disabled);
		txtPostfix.setDisable(disabled);
		txtExplicitBinding.setDisable(disabled);
		txtListBinding.setDisable(disabled);
	}

	/**
	 * Clear the advance fields
	 */
	public void clearAdvancedFields(){
		txtFromRegex.clear();
		txtToRegex.clear();
		lblKeyToMap.setText("Key: ");
		lblFromXPath.setText("Select an XML Element first");
		lblToXPath.setText("Select a JPSearch element first");

		txtPrefix.clear();
		txtPostfix.clear();
		txtExplicitBinding.clear();
		txtListBinding.clear();
		tableVariableBinding.getItems().clear();
	}

	/**
	 * Show details of the selected rule from the list
	 */
	public void showRuleDetails(){
		accAdvanced.setDisable(false);
		btnCancel.setDisable(false);
		btnSaveToMap.setDisable(false);
		treeXmlElements.setDisable(true);
		treeJPSElements.setDisable(true);
		setDisableAdvancedFields(false);

		//to trick the gui
		created = true; 

		String fromXPath;
		String fromRegex;
		String toXPath;
		String toRegex;
		List<VarBinding> vb;

		switch(currentRuleType){
		case ONETOONE:

			//Get data
			fromXPath = currentNewOneToOne.getFromXPath();
			fromRegex = currentNewOneToOne.getFromRegEx();
			toXPath = currentNewOneToOne.getToXPath();
			toRegex = currentNewOneToOne.getToRegEx();
			vb = currentNewOneToOne.getVariableBinding(); 

			tableXMLElements.getItems().clear();
			xmlElements.clear();

			xmlElements.add(new Element(true, fromXPath));	
			xmlElementsSelection.add(fromXPath);
			tableXMLElements.setItems(xmlElements);

			tableJPSearchElements.getItems().clear();
			jpsearchElements.clear();

			jpsearchElements.add(new JPSearchElement(true, toXPath));
			jpsearchElementsSelection.add(toXPath);

			tableJPSearchElements.setItems(jpsearchElements);

			lblRuleType.setText("OneToOne");
			lblFromXPath.setText(fromXPath);
			txtFromRegex.setText(fromRegex);
			lblToXPath.setText(toXPath);
			txtToRegex.setText(toRegex);

			tableVariableBinding.getItems().clear();
			if(vb != null)
				for(VarBinding v : vb){
					tableVariableBinding.getItems().add(v);
				}	

			lblKeyToMap.setText("Key: " + fromXPath);

			break;

		case ONETOMANY:

			//Get data
			fromXPath = currentNewOneToMany.getFromXPath();
			fromRegex = currentNewOneToMany.getFromRegEx();
			vb = currentNewOneToMany.getVariableBinding(); 
			Map<String, String> toFields = currentNewOneToMany.getToFields();		


			tableXMLElements.getItems().clear();
			xmlElements.clear();

			xmlElements.add(new Element(true, fromXPath));	
			xmlElementsSelection.add(fromXPath);
			tableXMLElements.setItems(xmlElements);			

			List<JPSearchElement> added = new ArrayList<JPSearchElement>(); //to be used for the selection of the first to field

			tableJPSearchElements.getItems().clear();
			jpsearchElements.clear();

			Iterator<Map.Entry<String, String>> it = toFields.entrySet().iterator();
			while (it.hasNext()) {	
				Map.Entry<String, String> pair = it.next();
				JPSearchElement newJPSElement = new JPSearchElement(true, pair.getKey());
				jpsearchElements.add(newJPSElement);
				jpsearchElementsSelection.add(pair.getValue());
				added.add(newJPSElement);
			}

			tableJPSearchElements.setItems(jpsearchElements);

			lblRuleType.setText("OneToMany");
			lblFromXPath.setText(fromXPath);
			txtFromRegex.setText(fromRegex);

			int firstIndex = tableJPSearchElements.getItems().indexOf(added.get(0));
			tableJPSearchElements.getSelectionModel().select(firstIndex);
			toXPath = added.get(0).nameProperty().get();
			toRegex = toFields.get(toXPath);

			lblToXPath.setText(toXPath);
			txtToRegex.setDisable(false);
			txtToRegex.setText(toRegex);

			tableVariableBinding.getItems().clear();
			if(vb != null)
				for(VarBinding v : vb){
					tableVariableBinding.getItems().add(v);
				}	

			lblKeyToMap.setText("Key: " + fromXPath);

			break;

		case MANYTOONE:

			//Get data
			Map<String, String> fromFields = currentNewManyToOne.getFromRegEx();
			toXPath = currentNewManyToOne.getToXPath();
			toRegex = currentNewManyToOne.getToRegEx();

			List<Element> m2oAdded = new ArrayList<Element>(); //to be used for the selection of the first to field

			Iterator<Map.Entry<String, String>> itM2O = fromFields.entrySet().iterator();
			while (itM2O.hasNext()) {	
				Map.Entry<String, String> pair = itM2O.next();
				Element newElement = new Element(true, pair.getKey());
				xmlElements.add(newElement);	
				xmlElementsSelection.add(pair.getKey());
				m2oAdded.add(newElement);
			}
			tableXMLElements.setItems(xmlElements);

			tableJPSearchElements.getItems().clear();
			jpsearchElements.clear();

			jpsearchElements.add(new JPSearchElement(true, toXPath));
			jpsearchElementsSelection.add(toXPath);

			tableJPSearchElements.setItems(jpsearchElements);

			lblRuleType.setText("ManyToOne");		

			int m2oFirstIndex = tableXMLElements.getItems().indexOf(m2oAdded.get(0));
			tableXMLElements.getSelectionModel().select(m2oFirstIndex);		
			fromXPath = m2oAdded.get(0).nameProperty().get();
			fromRegex = fromFields.get(fromXPath);
			lblFromXPath.setText(fromXPath);
			txtFromRegex.setText(fromRegex);

			lblToXPath.setText(toXPath);
			txtToRegex.setText(toRegex);
			lblKeyToMap.setText("Key: " + fromXPath);

			//VARIABLE BIDNING DETAILS ARE SHOWN WHEN XML ELEMENTS TABLE ROW IS SELECTED (CODE IN THAT SECTION)

			break;
		}
	}
}
